#!/usr/bin/env python
# encoding:utf8

import os
import sys
import pdb
import time
import traceback
from subprocess import Popen, PIPE

from multiprocessing import Process
from threading import Thread
from datetime import datetime, date

from scheduler.parameter_parser import parse_parameter
from scheduler.function_parser import parse_function
from scheduler.get_trigger import get_trigger
from triggers.util import convert_to_datetime
from utils.dec_ignore_exception import ignore_exception


from config import PYTHON_PATH, BASE_PATH


class TaskerWrapper(object):
    def __init__(self, tasker):
        self.tasker = tasker
        self.id = tasker.id
        self.title = tasker.title
        self.app_name = tasker.func
        self.args = tasker.args

        self.cron_type = tasker.cron_type
        self.cron_params = tasker.cron_params
        self.process = None  # main

    def get_trigger(self):
        return get_trigger(self.cron_type, self.cron_params)

    def run_instance(self):
        # self.process = Process(name=self.app_name, target=self.func_wrapper, args=(self.app_name, self.args))
        # self.process.daemon = True
        # self.process.start()
        # script_path = os.path.join(BASE_PATH, 'taskers', )

        script_path = self.app_name
        path, file = os.path.split(script_path)
        if not path.startswith("/"):
            path = os.path.join(BASE_PATH, path)
        script_file = os.path.join(path, file)

        # print "{}, {}, {}".format(PYTHON_PATH, path, script_file)
        self.process = Popen([PYTHON_PATH, script_file], stdout=PIPE, stderr=PIPE, cwd=path)
        # self.process = Popen([PYTHON_PATH, script_file], stdout=PIPE, stderr=PIPE)

        th = Thread(target=self.check_status, args=(self.process, ))
        th.setDaemon(True)
        th.start()

    def check_status(self, p):
        pid = p.pid
        self.update_tasker(pid=pid, start_time=datetime.now(), end_time=None, status=10, info="")
        while 1:
            code = p.poll()
            if code is None:  # still running
                time.sleep(1)
                continue
            else:
                break
        log_info = p.stdout.readlines()
        error_info = p.stderr.readlines()
        log_info_str = "".join(log_info) if isinstance(log_info, list) else log_info
        error_info_str = "".join(error_info) if isinstance(error_info, list) else error_info

        info = "{}\n\n{}".format(log_info_str, error_info_str)
        code = p.returncode
        if code != 0 and error_info:
            info = "return code: {}\n\n{}".format(code, info)
            self.update_tasker(pid="", end_time=None, status=-1, info=info)
        else:
            info = "return code: {}\n\n{}".format(code, info)
            self.update_tasker(pid="", end_time=None, status=0, info=info)

    # def func_wrapper(self, app_name, args):
    #     try:
    #         func = parse_function(app_name)
    #         args = parse_parameter(args)
    #     except Exception, ex:
    #         info = traceback.format_exc()
    #         self.update_tasker(pid="", status=-1, info=info)
    #         return
    #
    #     try:
    #         # 解析函数
    #         self.update_tasker(pid=os.getpid(), start_time=datetime.now(), end_time=None, status=10, info="")
    #         data = func(**args)
    #         self.update_tasker(pid="", end_time=datetime.now(), info="Done", status=0)
    #     except Exception, ex:
    #         info = traceback.format_exc()
    #         self.update_tasker(pid="", end_time=None, status=-1, info=info)

    def update_tasker(self, **kwargs):
        update_fields = []
        for k, v in kwargs.iteritems():
            setattr(self.tasker, k, v)
            update_fields.append(k)
        if update_fields:
            self.tasker.save(update_fields=update_fields)

    @property
    def last_fire_time(self):
        return self.tasker.start_time

    @last_fire_time.setter
    def last_fire_time(self, value):
        self.tasker.start_time = value
        self.tasker.save(update_fields=['start_time'])

    @property
    def status(self):
        return self.tasker.status

    @status.setter
    def status(self, value):
        self.tasker.status = value
        self.tasker.save(update_fields=['status'])

    @property
    def is_running(self):
        if self.process and self.process.poll() is None:
            return True
        return False

    # @ignore_exception
    # @property
    # def pid(self):
    #     if self.is_running:
    #         return self.process.pid
    #     return None

    def clear(self):
        if not self.is_running:
            self.process = None

    @ignore_exception
    def shutdown(self):
        if self.process and self.is_running:
            self.process.kill()
            n = datetime.now()
            info = "killed manually"
            self.update_tasker(pid="", end_time=n, status=-1, info=info)
        self.clear()

    def info(self):
        return "id:[%s] name:[%s] type:[%s] params:[%s]"%(
            self.id, self.title, self.cron_type, self.cron_params)

    def get_next_fire_time(self, last_fire_time, now):
        # 这个地方的task是父类的tasker
        try:
            trigger = self.get_trigger()
        except Exception, ex:
            info = traceback.format_exc()
            n = datetime.now()
            self.update_tasker(pid="", start_time=n, end_time=n, status=-1, info=info)
            return
        # should be of type with timezone
        last_fire_time = convert_to_datetime(last_fire_time)
        now = convert_to_datetime(now)
        return trigger.get_next_fire_time(last_fire_time, now)




# deprecated
class TimerCronJobThread(object):
    ''' job timer for func starting
    :param func:
    :param args:
    :param kargs:
    :param timer_options: (hour, minute, second)
    :return:
    '''
    def __init__(self, name, func, args=(), kwargs={}, hour=8, minute=0, second=0):
        self.name = name
        self.func = func
        self.args = args
        self.kwargs = kwargs

        self.hour = self.check_time(hour, 0, 24, 0)
        self.minute = self.check_time(minute, 0, 60, 0)
        self.second = self.check_time(second, 0, 60, 0)

        self.process = None
        self.last_run_date = None
        self.today_finished = False

    def run(self):
        today = date.today()
        if today != self.last_run_date:
            self.run_instance()

    def run_instance(self):
        if not self.is_running:
            self.process = Thread(name=self.name, target=self.func, args=self.args, kwargs=self.kwargs)
            self.process.setDaemon(True)
            self.last_run_date = date.today()
            self.process.start()

    @property
    def is_running(self):
        print >>sys.stdout, ">>>>", self.process, self.process.isAlive()
        if self.process and self.process.isAlive():
            return True
        return False

    @ignore_exception
    @property
    def pid(self):
        if self.is_running:
            return self.process.pid
        return None

    def get_start_time(self):
        return "%s%s%s"%(self.hour, self.minute, self.second)

    def check_time(self, value, min, max, default):
        # to "08"/'20'
        if not value: value = 0
        try:
            value = int(value)
        except:
            value = default
        if value > max: value = max
        if value < min: value = min
        return "{0:0>2}".format(value)

    def clear(self):
        if not self.is_running:
            self.process = None

    @ignore_exception
    def shutdown(self):
        # TODO
        raise NotImplementedError("thread could not be shutdown")

    def info(self):
        return "name:[%s] st:[%s]"%(self.name, self.get_start_time())